"""
Market Data Service API Routes - V2
====================================

基于插件化适配器架构的市场数据服务API路由

特点：
- 完全基于 MarketDataServiceV2
- 支持多数据源（Tushare, CTP, InfluxDB）
- 配置驱动的数据源选择
- 统一的错误处理
- WebSocket实时推送支持

作者: 架构组
版本: v2.0
更新日期: 2025-10-05
"""
import logging
import asyncio
from fastapi import APIRouter, Depends, HTTPException, Query, status, WebSocket, WebSocketDisconnect
from typing import List, Optional
from datetime import date, datetime
import uuid

# 导入 V2 服务
from ..services.core.market_data_service_v2 import MarketDataServiceV2

# 导入依赖注入函数（使用新的依赖注入模块）
from ..dependencies import get_market_data_service as get_market_data_service_v2

# 导入WebSocket服务
from ..services.websocket.service import websocket_service

router = APIRouter(prefix="/market", tags=["Market Data V2"])
logger = logging.getLogger(__name__)

@router.get("/health")
async def health_check():
    """市场数据服务健康检查"""
    return {
        "status": "healthy",
        "service": "market-data-service-v2",
        "version": "2.0.0",
        "timestamp": datetime.now().isoformat()
    }

@router.get("/ping")
async def ping():
    """简单的ping端点"""
    return {"message": "pong", "version": "2.0.0", "timestamp": datetime.now().isoformat()}

@router.get("/info")
async def service_info():
    """服务信息"""
    return {
        "name": "Market Data Service V2",
        "version": "2.0.0",
        "architecture": "plugin-based-adapters",
        "description": "基于插件化适配器架构的专业市场数据服务API",
        "features": [
            "多数据源支持（Tushare, CTP, InfluxDB）",
            "配置驱动的数据源切换",
            "完全解耦的服务设计",
            "实时数据订阅",
            "历史数据查询",
            "自动数据持久化"
        ],
        "endpoints": [
            "/health - 健康检查",
            "/ping - 连通性测试",
            "/info - 服务信息",
            "/adapters - 适配器信息",
            "/bars/historical - 历史K线数据",
            "/index-bars - 指数K线数据（前端图表专用）",
            "/ticks/realtime - 实时Tick数据",
            "/index - 单个指数查询",
            "/indices - 批量指数查询",
            "/indexes - 批量指数查询（仪表盘专用）"
        ]
    }

@router.get("/adapters")
async def get_adapters_info(
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """获取所有适配器的信息"""
    try:
        adapters = service.get_available_adapters()
        adapter_info = service.get_adapter_info()
        
        return {
            "success": True,
            "total": len(adapters),
            "adapters": adapters,
            "details": adapter_info,
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取适配器信息失败: {str(e)}")

# =================================================================
# 历史K线数据端点 (V2 API)
# =================================================================

@router.get("/bars/historical", status_code=status.HTTP_200_OK)
async def get_historical_bars(
    symbol: str = Query(..., description="标的代码（如：000001.SZ, 600000.SH）"),
    start_date: str = Query(..., description="开始日期 (YYYY-MM-DD)"),
    end_date: str = Query(..., description="结束日期 (YYYY-MM-DD)"),
    frequency: str = Query("1d", description="频率（1d=日线, 1h=小时线, 1m=分钟线）"),
    source: str = Query("tushare", description="数据源（tushare, influxdb, ctp）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取历史K线数据（V2 API）
    
    支持的数据源：
    
    - tushare: Tushare数据源（股票、指数历史数据）
    - influxdb: InfluxDB数据库（已存储的数据）
    - ctp: CTP数据源（期货数据）
    
    支持的标的代码：
    - 股票: 000001.SZ, 600000.SH
    - 指数: 000001.SH (上证指数), 399001.SZ (深证指数)
    - 期货: IF2312, IC2312 等
    """
    try:
        from datetime import datetime as dt
        start = dt.strptime(start_date, "%Y-%m-%d")
        end = dt.strptime(end_date, "%Y-%m-%d")
        
        # 调用 V2 服务
        bars = await service.get_historical_bars(
            symbol=symbol,
            start_time=start,
            end_time=end,
            frequency=frequency,
            source=source
        )
        
        # 转换为字典格式
        bars_data = [
            {
                "symbol": bar.symbol,
                "timestamp": bar.timestamp.isoformat(),
                "open": float(bar.open),
                "high": float(bar.high),
                "low": float(bar.low),
                "close": float(bar.close),
                "volume": float(bar.volume) if bar.volume else None,
                "amount": float(bar.amount) if bar.amount else None,
            }
            for bar in bars
        ]
        
        return {
            "success": True,
            "data": bars_data,
            "count": len(bars_data),
            "query": {
                "symbol": symbol,
                "start_date": start_date,
                "end_date": end_date,
                "frequency": frequency,
                "source": source
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取历史K线数据失败: {str(e)}")

# =================================================================
# 指数实时查询端点 (V2 API)
# =================================================================

@router.get("/index", status_code=status.HTTP_200_OK)
async def get_index_data(
    code: str = Query(..., description="指数代码（如：000001.SH, 399001.SZ, 399006.SZ, HSI）"),
    source: str = Query("tushare", description="数据源（tushare）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取指数实时数据（V2 API）
    
    支持的指数代码：
    - 000001.SH: 上证指数
    - 399001.SZ: 深证成指
    - 399006.SZ: 创业板指数
    - HSI: 恒生指数
    
    返回数据包括：
    - 当前点位
    - 涨跌点数
    - 涨跌幅
    - 最高/最低点
    - 成交量等
    """
    try:
        from datetime import datetime as dt, timedelta
        
        # 获取最近一个交易日的数据（使用历史数据API获取最新数据）
        end_date = dt.now()
        start_date = end_date - timedelta(days=7)  # 获取最近7天，确保能拿到最新交易日
        
        # 调用历史数据接口获取最新数据
        bars = await service.get_historical_bars(
            symbol=code,
            start_time=start_date,
            end_time=end_date,
            frequency="1d",
            source=source
        )
        
        # 如果tushare失败且是HSI，尝试使用akshare
        if not bars and (code == "HSI" or code == "^HSI"):
            try:
                logger.info(f"[{code}] Tushare无数据，尝试使用akshare...")
                bars = await service.get_historical_bars(
                    symbol=code,
                    start_time=start_date,
                    end_time=end_date,
                    frequency="1d",
                    source="akshare"  # 使用akshare作为备选数据源
                )
                if bars:
                    logger.info(f"[{code}] 使用akshare成功获取数据")
            except Exception as akshare_error:
                logger.error(f"[{code}] akshare也失败: {str(akshare_error)}")
        
        if not bars:
            return {
                "success": False,
                "message": f"未找到指数 {code} 的数据",
                "data": None,
                "timestamp": datetime.now().isoformat()
            }
        
        # 获取最新的一条数据
        latest_bar = bars[-1]
        
        # 计算涨跌
        prev_close = bars[-2].close if len(bars) >= 2 else latest_bar.open
        change = float(latest_bar.close) - float(prev_close)
        change_percent = (change / float(prev_close)) * 100 if prev_close else 0
        
        # 构建响应数据
        index_data = {
            "code": code,
            "name": _get_index_name(code),
            "current": float(latest_bar.close),
            "change": round(change, 2),
            "change_pct": round(change_percent, 2),
            "high": float(latest_bar.high),
            "low": float(latest_bar.low),
            "open": float(latest_bar.open),
            "close": float(latest_bar.close),
            "volume": float(latest_bar.volume) if latest_bar.volume else 0,
            "timestamp": latest_bar.timestamp.isoformat()
        }
        
        return {
            "success": True,
            "data": index_data,
            "source": source,
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        import traceback
        error_detail = f"获取指数数据失败: {str(e)}\n{traceback.format_exc()}"
        raise HTTPException(status_code=500, detail=error_detail)


def _get_index_name(code: str) -> str:
    """根据代码获取指数名称"""
    index_names = {
        "000001.SH": "上证指数",
        "399001.SZ": "深证成指",
        "399006.SZ": "创业板指数",
        "HSI": "恒生指数",
        "000016.SH": "上证50",
        "000300.SH": "沪深300",
        "000905.SH": "中证500"
    }
    return index_names.get(code, code)


# =================================================================
# 批量获取指数数据端点
# =================================================================

@router.get("/indices", status_code=status.HTTP_200_OK)
async def get_multiple_indices(
    codes: str = Query(..., description="指数代码列表，逗号分隔（如：000001.SH,399001.SZ,399006.SZ）"),
    source: str = Query("tushare", description="数据源（tushare）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    批量获取多个指数的实时数据
    
    示例：/api/v1/indices?codes=000001.SH,399001.SZ,399006.SZ
    """
    try:
        code_list = [code.strip() for code in codes.split(",")]
        results = []
        
        for code in code_list:
            try:
                from datetime import datetime as dt, timedelta
                
                end_date = dt.now()
                start_date = end_date - timedelta(days=7)
                
                bars = await service.get_historical_bars(
                    symbol=code,
                    start_time=start_date,
                    end_time=end_date,
                    frequency="1d",
                    source=source
                )
                
                if bars:
                    latest_bar = bars[-1]
                    prev_close = bars[-2].close if len(bars) >= 2 else latest_bar.open
                    change = float(latest_bar.close) - float(prev_close)
                    change_percent = (change / float(prev_close)) * 100 if prev_close else 0
                    
                    results.append({
                        "code": code,
                        "name": _get_index_name(code),
                        "current": float(latest_bar.close),
                        "change": round(change, 2),
                        "change_percent": round(change_percent, 2),
                        "high": float(latest_bar.high),
                        "low": float(latest_bar.low),
                        "open": float(latest_bar.open),
                        "close": float(latest_bar.close),
                        "volume": float(latest_bar.volume) if latest_bar.volume else 0,
                        "timestamp": latest_bar.timestamp.isoformat()
                    })
                else:
                    results.append({
                        "code": code,
                        "name": _get_index_name(code),
                        "error": "数据不可用"
                    })
                    
            except Exception as e:
                results.append({
                    "code": code,
                    "name": _get_index_name(code),
                    "error": str(e)
                })
        
        return {
            "success": True,
            "data": results,
            "count": len(results),
            "source": source,
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"批量获取指数数据失败: {str(e)}")


# =================================================================
# 实时数据端点 (V2 API)
# =================================================================

@router.get("/ticks/realtime", status_code=status.HTTP_200_OK)
async def get_realtime_ticks(
    symbol: str = Query(..., description="标的代码（支持单个或逗号分隔的多个代码）"),
    source: str = Query("tushare", description="数据源（tushare, akshare, ctp）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取实时Tick数据（V2 API）
    
    支持的数据源：
    - tushare: Tushare实时数据（股票和指数，推荐，纯爬虫无需积分）
    - akshare: AKShare实时数据（股票和指数）
    - ctp: CTP实时数据源（期货）
    
    示例：
    - 单个股票: /api/v1/ticks/realtime?symbol=000001.SH&source=tushare
    - 多个股票: /api/v1/ticks/realtime?symbol=000001.SH,399001.SZ,000300.SH&source=tushare
    """
    try:
        # 调用 V2 服务
        ticks = await service.get_realtime_ticks(
            symbol=symbol,
            source=source
        )
        
        # 转换为字典格式
        ticks_data = [
            {
                "symbol": tick.symbol,
                "timestamp": tick.timestamp.isoformat(),
                "price": float(tick.price),
                "volume": float(tick.volume) if tick.volume else None,
                "bid_price": float(tick.bid_price) if tick.bid_price else None,
                "ask_price": float(tick.ask_price) if tick.ask_price else None,
                "bid_volume": float(tick.bid_volume) if tick.bid_volume else None,
                "ask_volume": float(tick.ask_volume) if tick.ask_volume else None,
            }
            for tick in ticks
        ]
        
        return {
            "success": True,
            "data": ticks_data,
            "count": len(ticks_data),
            "query": {
                "symbol": symbol,
                "source": source
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取实时Tick数据失败: {str(e)}")

# =================================================================
# 订阅管理端点 (V2 API)
# =================================================================

@router.post("/subscribe", status_code=status.HTTP_200_OK)
async def subscribe_symbol(
    symbol: str = Query(..., description="标的代码"),
    data_type: str = Query("tick", description="数据类型（tick, bar, orderbook）"),
    source: str = Query("ctp", description="数据源"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    订阅实时数据（V2 API）
    
    订阅后，数据将通过WebSocket推送
    """
    try:
        success = await service.subscribe(
            symbol=symbol,
            data_type=data_type,
            source=source
        )
        
        return {
            "success": success,
            "message": f"成功订阅 {symbol} 的 {data_type} 数据",
            "subscription": {
                "symbol": symbol,
                "data_type": data_type,
                "source": source
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"订阅失败: {str(e)}")

@router.delete("/unsubscribe", status_code=status.HTTP_200_OK)
async def unsubscribe_symbol(
    symbol: str = Query(..., description="标的代码"),
    data_type: str = Query("tick", description="数据类型"),
    source: str = Query("ctp", description="数据源"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """取消订阅实时数据（V2 API）"""
    try:
        success = await service.unsubscribe(
            symbol=symbol,
            data_type=data_type,
            source=source
        )
        
        return {
            "success": success,
            "message": f"成功取消订阅 {symbol} 的 {data_type} 数据",
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"取消订阅失败: {str(e)}")

# =================================================================
# 服务状态端点 (V2 API)
# =================================================================

@router.get("/status")
async def get_service_status(
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """获取服务详细状态（V2 API）"""
    try:
        health_status = await service.health_check()
        return {
            "success": True,
            "status": health_status,
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取服务状态失败: {str(e)}")


# =================================================================
# 演示端点 (Demo API)
# =================================================================

@router.get("/index-bars", status_code=status.HTTP_200_OK)
async def get_index_bars(
    code: str = Query(..., description="指数代码（如：000001.SH, 399001.SZ）"),
    period: str = Query("5m", description="K线周期（1m/5m/15m/30m/60m=分钟线, 1d=日线）"),
    limit: int = Query(120, description="返回最多N条数据", ge=1, le=10000),
    source: str = Query("tushare", description="数据源（tushare, akshare）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取指数K线数据（前端图表专用）
    
    参数：
    - code: 指数代码（000001.SH, 399001.SZ, 399006.SZ, HSI等）
    - period: K线周期（1m/5m/15m/30m/60m=分钟线, 1d=日线）
    - limit: 最多返回N条数据
    - source: 数据源（tushare=日线, akshare=分钟线）
    
    返回格式（与前端IndexChart组件匹配）：
    {
        "code": "000001.SH",
        "name": "上证指数",
        "period": "5m",
        "bars": [...],
        "total": 100
    }
    """
    try:
        from datetime import datetime as dt, timedelta
        
        # 1. 根据周期计算查询天数
        if period in ["1m"]:
            days = 1  # 1分钟线查1天
        elif period in ["5m", "15m"]:
            days = 3  # 5/15分钟线查3天
        elif period in ["30m", "60m", "1h"]:
            days = 7  # 30分钟/1小时线查7天
        else:  # 日线、周线
            days = 120  # 日线查120天
        
        # 2. 计算时间范围
        end_date = dt.now()
        start_date = end_date - timedelta(days=days)
        
        # 3. 根据周期和数据源自动调整
        actual_source = source
        if period in ["1m", "5m", "15m", "30m", "60m", "1h"]:
            # 分钟级数据使用AkShare（免费）
            actual_source = "akshare"
            # AkShare使用纯代码（不带交易所后缀）
            code_parts = code.split(".")
            symbol_code = code_parts[0]  # 例如：000001
        else:
            # 日线数据可以使用Tushare
            symbol_code = code
        
        # 4. 调用服务获取数据
        logger.info(f"[index-bars] 请求K线：code={code}, period={period}, source={actual_source}, symbol={symbol_code}")
        
        bars = await service.get_historical_bars(
            symbol=symbol_code,
            start_time=start_date,
            end_time=end_date,
            frequency=period,
            source=actual_source
        )
        
        # 5. 数据验证
        if not bars:
            logger.warning(f"[index-bars] 未获取到数据：code={code}, period={period}")
            return {
                "code": code,
                "name": _get_index_name(code),
                "period": period,
                "bars": [],
                "total": 0
            }
        
        # 6. 限制返回数量（取最新的N条）
        if len(bars) > limit:
            bars = bars[-limit:]
        
        # 7. 转换为前端期望的格式
        bars_data = [
            {
                "timestamp": bar.timestamp.isoformat(),
                "open": float(bar.open),
                "high": float(bar.high),
                "low": float(bar.low),
                "close": float(bar.close),
                "volume": float(bar.volume) if bar.volume else 0,
            }
            for bar in bars
        ]
        
        logger.info(f"[index-bars] 成功返回 {len(bars_data)} 条K线数据")
        
        # 8. 返回前端期望的格式
        return {
            "code": code,
            "name": _get_index_name(code),
            "period": period,
            "bars": bars_data,
            "total": len(bars_data)
        }
        
    except ValueError as e:
        logger.error(f"[index-bars] 参数错误: {str(e)}")
        raise HTTPException(status_code=400, detail=f"参数错误: {str(e)}")
    except Exception as e:
        import traceback
        error_detail = f"获取指数K线数据失败: {str(e)}\n{traceback.format_exc()}"
        logger.error(f"[index-bars] {error_detail}")
        raise HTTPException(status_code=500, detail=error_detail)


@router.get("/demo/index-bars", status_code=status.HTTP_200_OK)
async def get_demo_index_bars(
    days: int = Query(1, description="查询最近N天的数据（分钟线建议1-7天，日线可30-365天）", ge=1, le=365),
    limit: int = Query(500, description="返回最多N条数据", ge=1, le=10000),
    frequency: str = Query("5m", description="K线周期（1m/5m/15m/30m/60m=分钟线, 1d=日线, 1w=周线）"),
    source: str = Query("akshare", description="数据源（akshare=免费分钟线, tushare=日线）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取上证指数K线数据（演示用）
    
    这是一个专门为前端演示设计的端点，提供开箱即用的上证指数K线数据展示。
    
    特点：
    - 固定标的：上证指数 (000001.SH)
    - 简化参数：仅需指定天数和频率
    - 自动计算：自动计算统计信息
    - 友好格式：返回适合前端图表库的数据格式
    - 免费数据：使用AkShare提供分钟级K线（完全免费，无需Token）
    
    参数：
    - days: 查询最近N天的数据（分钟线建议1-7天，日线可30-365天）
    - limit: 最多返回N条数据（默认500条，适合分钟线）
    - frequency: K线周期（1m/5m/15m/30m/60m=分钟线, 1d=日线, 1w=周线）
    - source: 数据源
      * akshare: 免费，支持分钟线和日线（推荐）
      * tushare: 仅支持日线、周线、月线
    
    返回格式：
    {
        "success": true,
        "data": {
            "symbol": "000001.SH",
            "name": "上证指数",
            "frequency": "5m",
            "source": "akshare",
            "bars": [...],
            "statistics": {...}
        }
    }
    """
    try:
        from datetime import datetime as dt, timedelta
        
        # 1. 计算时间范围
        end_date = dt.now()
        start_date = end_date - timedelta(days=days)
        
        # 2. 根据数据源自动调整参数
        actual_source = source
        if frequency in ["1m", "5m", "15m", "30m", "60m"]:
            # 分钟级数据强制使用AkShare
            if source == "tushare":
                actual_source = "akshare"
                logger.warning(f"Tushare不支持分钟级数据，自动切换到AkShare")
        
        # 3. 调用 V2 服务获取数据
        bars = await service.get_historical_bars(
            symbol="000001",  # AkShare使用纯代码
            start_time=start_date,
            end_time=end_date,
            frequency=frequency,
            source=actual_source
        )
        
        # 3. 数据验证
        if not bars:
            return {
                "success": False,
                "message": "未获取到数据（可能是非交易时间或周末）",
                "data": None,
                "timestamp": datetime.now().isoformat()
            }
        
        # 4. 限制返回数量（取最新的N条）
        if len(bars) > limit:
            bars = bars[-limit:]
        
        # 5. 转换为前端友好的格式
        bars_data = [
            {
                "timestamp": bar.timestamp.isoformat(),
                "open": float(bar.open),
                "high": float(bar.high),
                "low": float(bar.low),
                "close": float(bar.close),
                "volume": float(bar.volume) if bar.volume else 0,
                "amount": float(bar.amount) if bar.amount else 0,
            }
            for bar in bars
        ]
        
        # 6. 计算统计信息
        first_bar = bars[0]
        last_bar = bars[-1]
        
        # 价格统计
        all_highs = [float(bar.high) for bar in bars]
        all_lows = [float(bar.low) for bar in bars]
        period_high = max(all_highs)
        period_low = min(all_lows)
        
        # 涨跌幅计算
        price_change = float(last_bar.close) - float(first_bar.open)
        change_percent = (price_change / float(first_bar.open)) * 100 if first_bar.open else 0
        
        # 成交量统计
        total_volume = sum(float(bar.volume) if bar.volume else 0 for bar in bars)
        avg_volume = total_volume / len(bars) if bars else 0
        
        statistics = {
            "count": len(bars_data),
            "time_range": {
                "start": first_bar.timestamp.isoformat(),
                "end": last_bar.timestamp.isoformat()
            },
            "price_range": {
                "open": float(first_bar.open),
                "close": float(last_bar.close),
                "high": period_high,
                "low": period_low,
                "change": round(price_change, 2),
                "change_percent": round(change_percent, 2)
            },
            "volume": {
                "total": round(total_volume, 2),
                "average": round(avg_volume, 2)
            }
        }
        
        # 7. 构建响应
        return {
            "success": True,
            "data": {
                "symbol": "000001.SH",
                "name": "上证指数",
                "frequency": frequency,
                "frequency_display": _get_frequency_display(frequency),
                "source": actual_source,
                "source_display": "AkShare (免费)" if actual_source == "akshare" else "Tushare",
                "bars": bars_data,
                "statistics": statistics
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f"参数错误: {str(e)}")
    except Exception as e:
        import traceback
        error_detail = f"获取演示数据失败: {str(e)}\n{traceback.format_exc()}"
        raise HTTPException(status_code=500, detail=error_detail)


def _get_frequency_display(frequency: str) -> str:
    """获取频率的中文显示名称"""
    frequency_map = {
        "1m": "1分钟线",
        "5m": "5分钟线",
        "15m": "15分钟线",
        "30m": "30分钟线",
        "1h": "1小时线",
        "1d": "日线",
        "1w": "周线",
        "1M": "月线"
    }
    return frequency_map.get(frequency, frequency)


# =================================================================
# 前端仪表盘专用端点
# =================================================================

@router.get("/indexes", status_code=status.HTTP_200_OK)
async def get_market_indexes(
    codes: Optional[str] = Query(None, description="指数代码列表，逗号分隔（如：000001.SH,399001.SZ）。如果不提供，返回默认4大指数"),
    source: str = Query("tushare", description="数据源（tushare）"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取多个国内市场指数数据（前端仪表盘专用）
    
    这是一个为前端Dashboard设计的端点，提供4大核心指数的实时数据。
    支持的指数代码：
    - 000001.SH: 上证指数
    - 399001.SZ: 深证成指
    - 399006.SZ: 创业板指数
    - HSI: 恒生指数
    
    不传codes参数时，默认返回以上4大指数。
    """
    try:
        # 默认4大指数
        default_codes = ["000001.SH", "399001.SZ", "399006.SZ", "HSI"]
        code_list = [code.strip() for code in codes.split(",")] if codes else default_codes
        
        from datetime import datetime as dt, timedelta
        
        results = []
        for code in code_list:
            try:
                logger.info(f"[{code}] 开始获取数据，使用数据源: {source}")
                end_date = dt.now()
                start_date = end_date - timedelta(days=7)
                
                bars = await service.get_historical_bars(
                    symbol=code,
                    start_time=start_date,
                    end_time=end_date,
                    frequency="1d",
                    source=source
                )
                
                logger.info(f"[{code}] 获取到 {len(bars) if bars else 0} 条数据")
                
                if bars and len(bars) > 0:
                    latest_bar = bars[-1]
                    prev_close = bars[-2].close if len(bars) >= 2 else latest_bar.open
                    change = float(latest_bar.close) - float(prev_close)
                    change_percent = (change / float(prev_close)) * 100 if prev_close else 0
                    
                    results.append({
                        "code": code,
                        "name": _get_index_name(code),
                        "current": float(latest_bar.close),
                        "change": round(change, 2),
                        "change_pct": round(change_percent, 2),
                        "high": float(latest_bar.high),
                        "low": float(latest_bar.low),
                        "open": float(latest_bar.open),
                        "prev_close": float(prev_close),
                        "volume": float(latest_bar.volume) if latest_bar.volume else 0,
                        "amount": float(latest_bar.amount) if latest_bar.amount else 0,
                        "timestamp": latest_bar.timestamp.isoformat()
                    })
                else:
                    # 对于HSI等tushare不支持的指数，尝试使用akshare
                    if code == "HSI" or code == "^HSI":
                        try:
                            logger.info(f"尝试使用akshare获取 {code} 数据...")
                            bars = await service.get_historical_bars(
                                symbol=code,
                                start_time=start_date,
                                end_time=end_date,
                                frequency="1d",
                                source="akshare"  # 使用akshare作为备选数据源
                            )
                            
                            if bars and len(bars) > 0:
                                latest_bar = bars[-1]
                                prev_close = bars[-2].close if len(bars) >= 2 else latest_bar.open
                                change = float(latest_bar.close) - float(prev_close)
                                change_percent = (change / float(prev_close)) * 100 if prev_close else 0
                                
                                results.append({
                                    "code": code,
                                    "name": _get_index_name(code),
                                    "current": float(latest_bar.close),
                                    "change": round(change, 2),
                                    "change_pct": round(change_percent, 2),
                                    "high": float(latest_bar.high),
                                    "low": float(latest_bar.low),
                                    "open": float(latest_bar.open),
                                    "prev_close": float(prev_close),
                                    "volume": float(latest_bar.volume) if latest_bar.volume else 0,
                                    "amount": float(latest_bar.amount) if latest_bar.amount else 0,
                                    "timestamp": latest_bar.timestamp.isoformat()
                                })
                                logger.info(f"使用akshare成功获取 {code} 数据")
                                continue
                            else:
                                logger.warning(f"使用akshare获取 {code} 返回空数据")
                        except Exception as akshare_error:
                            logger.error(f"使用akshare获取 {code} 也失败: {str(akshare_error)}")
                    
                    # 如果都失败了，跳过这个指数（不添加占位数据）
                    logger.warning(f"无法获取 {code} 数据，跳过")
                    
            except Exception as e:
                logger.error(f"获取指数 {code} 数据失败（异常）: {str(e)}")
        
        return {
            "success": True,
            "data": results,
            "count": len(results),
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        import traceback
        error_detail = f"获取市场指数数据失败: {str(e)}\n{traceback.format_exc()}"
        logger.error(error_detail)
        raise HTTPException(status_code=500, detail=error_detail)


@router.get("/global-indexes", status_code=status.HTTP_200_OK)
async def get_global_indexes(
    regions: Optional[str] = Query(None, description="区域列表，逗号分隔（americas, europe, hongkong, asia）"),
    include_closed: bool = Query(True, description="是否包含已休市的市场"),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取全球主要市场指数数据
    
    返回美洲、欧洲、香港、亚太地区的主要指数数据。
    数据按区域分组返回。
    """
    try:
        import akshare as ak
        from datetime import datetime as dt, timedelta
        
        # 定义全球主要指数（AKShare代码映射）
        global_indexes = {
            "americas": [
                {"code": "^DJI", "name": "道琼斯", "exchange": "NYSE", "ak_code": ".DJI"},
                {"code": "^GSPC", "name": "标普500", "exchange": "NYSE", "ak_code": ".INX"},
                {"code": "^IXIC", "name": "纳斯达克", "exchange": "NASDAQ", "ak_code": ".IXIC"},
            ],
            "europe": [
                {"code": "^FTSE", "name": "富时100", "exchange": "LSE", "ak_code": "FTSE"},
                {"code": "^GDAXI", "name": "德国DAX", "exchange": "XETRA", "ak_code": "DAX"},
                {"code": "^FCHI", "name": "法国CAC40", "exchange": "Euronext", "ak_code": "CAC40"},
            ],
            "hongkong": [
                {"code": "HSI", "name": "恒生指数", "exchange": "HKEX", "ak_code": "HSI"},
                {"code": "HSCEI", "name": "恒生国企", "exchange": "HKEX", "ak_code": "HSCEI"},
                {"code": "HSTECH", "name": "恒生科技", "exchange": "HKEX", "ak_code": "HSTECH"},
            ],
            "asia": [
                {"code": "N225", "name": "日经225", "exchange": "TSE", "ak_code": "N225"},
                {"code": "000001.SH", "name": "上证指数", "exchange": "SSE", "ak_code": "000001"},
                {"code": "399001.SZ", "name": "深证成指", "exchange": "SZSE", "ak_code": "399001"},
                {"code": "399006.SZ", "name": "创业板指", "exchange": "SZSE", "ak_code": "399006"},
            ]
        }
        
        # 根据请求筛选区域
        selected_regions = regions.split(",") if regions else ["americas", "europe", "hongkong", "asia"]
        
        result_data = {}
        
        for region in selected_regions:
            if region not in global_indexes:
                continue
                
            region_data = []
            
            # 批量获取该区域的指数数据
            if region == "americas":
                # 美股指数 - 使用 akshare
                for index_info in global_indexes[region]:
                    try:
                        df = await asyncio.to_thread(ak.index_us_stock_sina, index_info["ak_code"])
                        if df is not None and not df.empty:
                            latest = df.iloc[-1]
                            prev = df.iloc[-2] if len(df) >= 2 else latest
                            
                            current = float(latest['close'])
                            prev_close = float(prev['close'])
                            change = current - prev_close
                            change_pct = (change / prev_close * 100) if prev_close != 0 else 0.0
                            
                            region_data.append({
                                "code": index_info["code"],
                                "name": index_info["name"],
                                "name_en": index_info["name"],
                                "exchange": index_info["exchange"],
                                "current": round(current, 2),
                                "change": round(change, 2),
                                "change_pct": round(change_pct, 2),
                                "open": float(latest['open']) if 'open' in latest else current,
                                "close": current,
                                "high": float(latest['high']) if 'high' in latest else current,
                                "low": float(latest['low']) if 'low' in latest else current,
                                "volume": int(latest['volume']) if 'volume' in latest else 0,
                                "amount": 0,
                                "market_status": "closed",
                                "timestamp": datetime.now().isoformat()
                            })
                        else:
                            raise ValueError("No data returned")
                    except Exception as e:
                        logger.warning(f"获取美股指数 {index_info['code']} 失败: {str(e)}")
                        region_data.append(_create_fallback_data(index_info))
                        
            elif region == "hongkong":
                # 港股指数 - 批量获取
                try:
                    df = await asyncio.to_thread(ak.stock_hk_index_spot_sina)
                    if df is not None and not df.empty:
                        for index_info in global_indexes[region]:
                            try:
                                row = df[df['代码'] == index_info["ak_code"]]
                                if not row.empty:
                                    row = row.iloc[0]
                                    current = float(row['最新价'])
                                    change = float(row['涨跌额'])
                                    change_pct = float(row['涨跌幅'])
                                    
                                    region_data.append({
                                        "code": index_info["code"],
                                        "name": index_info["name"],
                                        "name_en": index_info["name"],
                                        "exchange": index_info["exchange"],
                                        "current": round(current, 2),
                                        "change": round(change, 2),
                                        "change_pct": round(change_pct, 2),
                                        "open": float(row['今开']) if '今开' in row else current,
                                        "close": float(row['昨收']) if '昨收' in row else current,
                                        "high": float(row['最高']) if '最高' in row else current,
                                        "low": float(row['最低']) if '最低' in row else current,
                                        "volume": 0,
                                        "amount": 0,
                                        "market_status": "trading" if abs(change_pct) > 0 else "closed",
                                        "timestamp": datetime.now().isoformat()
                                    })
                                else:
                                    region_data.append(_create_fallback_data(index_info))
                            except Exception as e:
                                logger.warning(f"解析港股指数 {index_info['code']} 失败: {str(e)}")
                                region_data.append(_create_fallback_data(index_info))
                    else:
                        for index_info in global_indexes[region]:
                            region_data.append(_create_fallback_data(index_info))
                except Exception as e:
                    logger.error(f"批量获取港股指数失败: {str(e)}")
                    for index_info in global_indexes[region]:
                        region_data.append(_create_fallback_data(index_info))
                        
            elif region == "asia":
                # 亚太指数 - 使用现有接口获取A股，日经用模拟数据
                for index_info in global_indexes[region]:
                    try:
                        if index_info["code"] in ["000001.SH", "399001.SZ", "399006.SZ"]:
                            # A股指数 - 使用现有服务
                            ticks = await service.get_realtime_ticks(
                                symbol=index_info["code"],
                                source="tushare"
                            )
                            if ticks and len(ticks) > 0:
                                tick = ticks[0]
                                # 从 extra_fields 中获取数据（Tushare适配器将这些字段存储在extra_fields中）
                                extra = tick.extra_fields or {}
                                pre_close = float(extra.get('pre_close', 0))
                                current_price = float(tick.price)
                                
                                # 计算涨跌
                                change = current_price - pre_close if pre_close else 0
                                change_pct = (change / pre_close * 100) if pre_close and pre_close != 0 else 0
                                
                                region_data.append({
                                    "code": index_info["code"],
                                    "name": extra.get('name', index_info["name"]),
                                    "name_en": index_info["name"],
                                    "exchange": index_info["exchange"],
                                    "current": current_price,
                                    "change": round(change, 2),
                                    "change_pct": round(change_pct, 2),
                                    "open": float(extra.get('open', current_price)),
                                    "close": current_price,
                                    "high": float(extra.get('high', current_price)),
                                    "low": float(extra.get('low', current_price)),
                                    "volume": int(tick.volume) if tick.volume else 0,
                                    "amount": int(tick.total_amount) if tick.total_amount else 0,
                                    "market_status": "trading" if abs(change_pct) > 0 else "closed",
                                    "timestamp": tick.timestamp.isoformat()
                                })
                            else:
                                region_data.append(_create_fallback_data(index_info))
                        else:
                            # 日经等其他指数暂用模拟数据
                            region_data.append(_create_fallback_data(index_info))
                    except Exception as e:
                        logger.warning(f"获取亚太指数 {index_info['code']} 失败: {str(e)}")
                        region_data.append(_create_fallback_data(index_info))
                        
            elif region == "europe":
                # 欧洲指数 - 暂时用模拟数据
                for index_info in global_indexes[region]:
                    region_data.append(_create_fallback_data(index_info))
            
            result_data[region] = region_data
        
        return {
            "success": True,
            "data": result_data,
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        import traceback
        error_detail = f"获取全球指数数据失败: {str(e)}\n{traceback.format_exc()}"
        logger.error(error_detail)
        raise HTTPException(status_code=500, detail=error_detail)


def _create_fallback_data(index_info: dict) -> dict:
    """创建fallback数据（当真实数据获取失败时）"""
    return {
        "code": index_info["code"],
        "name": index_info["name"],
        "name_en": index_info["name"],
        "exchange": index_info.get("exchange", ""),
        "current": 0.0,
        "change": 0.0,
        "change_pct": 0.0,
        "open": 0.0,
        "close": 0.0,
        "high": 0.0,
        "low": 0.0,
        "volume": 0,
        "amount": 0,
        "market_status": "closed",
        "timestamp": datetime.now().isoformat()
    }


@router.get("/stock-rankings", status_code=status.HTTP_200_OK)
async def get_stock_rankings(
    limit: int = Query(10, description="每个榜单返回的数量", ge=1, le=50),
    service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    获取个股涨跌幅排行榜
    
    返回三个榜单：
    - gainers: 涨幅榜
    - losers: 跌幅榜
    - turnover: 成交额榜
    
    每个榜单包含前N只股票（由limit参数指定）
    """
    try:
        # 由于获取实时排行榜数据需要akshare或其他数据源的支持
        # 这里先返回模拟数据结构，后续可以对接真实数据源
        
        # 模拟涨幅榜数据
        gainers = []
        for i in range(limit):
            gainers.append({
                "rank": i + 1,
                "code": f"60{i:04d}",
                "name": f"示例股票{i+1}",
                "current": 10.00 + i * 0.5,
                "change_pct": 10.0 - i * 0.5,
                "volume": 1000000 * (limit - i),
                "amount": 10000000 * (limit - i)
            })
        
        # 模拟跌幅榜数据
        losers = []
        for i in range(limit):
            losers.append({
                "rank": i + 1,
                "code": f"00{i:04d}",
                "name": f"示例股票{i+1}",
                "current": 10.00 - i * 0.3,
                "change_pct": -10.0 + i * 0.5,
                "volume": 1000000 * (limit - i),
                "amount": 10000000 * (limit - i)
            })
        
        # 模拟成交额榜数据
        turnover = []
        for i in range(limit):
            turnover.append({
                "rank": i + 1,
                "code": f"30{i:04d}",
                "name": f"示例股票{i+1}",
                "current": 50.00 + i * 2,
                "change_pct": 2.0 - i * 0.3,
                "volume": 100000000 * (limit - i),
                "amount": 5000000000 * (limit - i)
            })
        
        return {
            "success": True,
            "data": {
                "gainers": gainers,
                "losers": losers,
                "turnover": turnover
            },
            "timestamp": datetime.now().isoformat(),
            "note": "当前为模拟数据，需要配置数据源以获取真实排行榜数据"
        }
        
    except Exception as e:
        import traceback
        error_detail = f"获取股票排行榜失败: {str(e)}\n{traceback.format_exc()}"
        logger.error(error_detail)
        raise HTTPException(status_code=500, detail=error_detail)


# ==================== WebSocket 端点 ====================

@router.websocket("/ws/market-data")
async def websocket_market_data(websocket: WebSocket):
    """
    WebSocket实时市场数据推送端点
    
    支持功能：
    - 实时股票数据推送
    - 订阅/取消订阅股票代码
    - 心跳检测
    - 连接管理
    """
    client_id = str(uuid.uuid4())
    
    try:
        # 建立WebSocket连接
        success = await websocket_service.connect(websocket, client_id)
        if not success:
            await websocket.close(code=1013, reason="Connection failed")
            return
        
        logger.info(f"WebSocket客户端 {client_id} 已连接")
        
        # 消息处理循环
        while True:
            try:
                # 接收客户端消息
                message = await websocket.receive_text()
                await websocket_service.handle_message(client_id, message)
                
            except WebSocketDisconnect:
                logger.info(f"WebSocket客户端 {client_id} 主动断开连接")
                break
            except Exception as e:
                logger.error(f"WebSocket消息处理错误: {e}", exc_info=True)
                await websocket_service._send_to_client(
                    client_id, 
                    "error", 
                    {"message": f"消息处理失败: {str(e)}"}
                )
                
    except Exception as e:
        logger.error(f"WebSocket连接处理失败: {e}", exc_info=True)
    finally:
        # 清理连接
        await websocket_service.disconnect(client_id)
        logger.info(f"WebSocket客户端 {client_id} 连接已清理")


@router.get("/ws/status")
async def websocket_status():
    """
    获取WebSocket服务状态
    
    Returns:
        dict: WebSocket服务状态信息
    """
    try:
        metrics = websocket_service.get_metrics()
        return {
            "success": True,
            "data": {
                "active_connections": websocket_service.get_connection_count(),
                "subscription_count": websocket_service.get_subscription_count(),
                "metrics": metrics
            },
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        logger.error(f"获取WebSocket状态失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"获取WebSocket状态失败: {str(e)}")


@router.post("/ws/broadcast")
async def websocket_broadcast(
    symbols: List[str],
    message: str,
    market_data_service: MarketDataServiceV2 = Depends(get_market_data_service_v2)
):
    """
    手动触发WebSocket数据广播（管理功能）
    
    Args:
        symbols: 要广播的股票代码列表
        message: 广播消息
        market_data_service: 市场数据服务
        
    Returns:
        dict: 广播结果
    """
    try:
        # 获取实时数据
        realtime_data = []
        for symbol in symbols:
            try:
                data = await market_data_service.get_realtime_data(symbol)
                realtime_data.append({
                    "symbol": symbol,
                    "data": data
                })
            except Exception as e:
                logger.warning(f"获取 {symbol} 实时数据失败: {e}")
                realtime_data.append({
                    "symbol": symbol,
                    "error": str(e)
                })
        
        # 广播数据
        success_count = 0
        for item in realtime_data:
            if "error" not in item:
                count = await websocket_service.broadcast_data(
                    item["symbol"], 
                    item["data"]
                )
                success_count += count
        
        return {
            "success": True,
            "data": {
                "message": message,
                "symbols": symbols,
                "broadcast_count": success_count,
                "realtime_data": realtime_data
            },
            "timestamp": datetime.now().isoformat()
        }
        
    except Exception as e:
        logger.error(f"WebSocket广播失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"WebSocket广播失败: {str(e)}")